// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.linqingying.gitcode.api

import com.intellij.collaboration.api.ServerPath
import com.intellij.openapi.util.NlsSafe
import com.intellij.openapi.util.text.StringUtil
import com.intellij.util.io.URLUtil
import com.intellij.util.xmlb.annotations.Attribute
import com.intellij.util.xmlb.annotations.Tag
import com.linqingying.gitcode.exceptions.GitCodeParseException
import org.jetbrains.annotations.NonNls
import java.net.URI
import java.net.URISyntaxException
import java.util.*
import java.util.regex.Pattern

/**
 * GitCode server reference allowing to specify custom port and path to instance
 */
@Tag("server")
class GitCodeServerPath @JvmOverloads constructor(
    @field:Attribute("useHttp") private val myUseHttp: Boolean? = null,
    host: @NonNls String = "",
    @field:Attribute("port") val port: Int? = null,
    @field:Attribute("suffix") val suffix: @NonNls String? = null
) :
    ServerPath {
    @Attribute("host")
    val host: String = StringUtil.toLowerCase(host)

    constructor(host: @NonNls String) : this(null, host, null, null)

    val schema: String
        get() = if (myUseHttp == null || !myUseHttp) "https" else "http"

    @JvmOverloads
    fun toUrl(showSchema: Boolean = true): String {
        val builder = StringBuilder()
        if (showSchema) builder.append(schemaUrlPart)
        builder.append(host).append(portUrlPart).append(
            StringUtil.notNullize(
                suffix
            )
        )
        return builder.toString()
    }

    val apiHost: String
        get() {
            return if (isGitCodeDotCom) {
                API_PREFIX + host
            } else {
                host
            }
        }

    fun toApiUrl(): String {
        val builder = StringBuilder(schemaUrlPart)

        if (isGitCodeDotCom) {
            builder.append(API_PREFIX).append(host).append(portUrlPart).append(API_SUFFIX).append(StringUtil.notNullize(suffix))
        } else {
            builder.append(host).append(portUrlPart).append(StringUtil.notNullize(suffix)).append(ENTERPRISE_API_SUFFIX)
        }

        return builder.toString()
    }


    override fun toURI(): URI {
        val port = port ?: -1
        try {
            return URI(schema, null, host, port, suffix, null, null)
        } catch (e: URISyntaxException) {
            // shouldn't happen, because we pre-validate the data
            throw RuntimeException(e)
        }
    }

    val isGitCodeDotCom: Boolean
        get() = host.equals(DEFAULT_HOST, ignoreCase = true)

    override fun toString(): @NlsSafe String {
        val schema = if (myUseHttp != null) schemaUrlPart else ""
        return schema + host + portUrlPart + StringUtil.notNullize(suffix)
    }

    private val portUrlPart: String
        get() = if (port != null) (":$port") else ""

    private val schemaUrlPart: String
        get() = schema + URLUtil.SCHEME_SEPARATOR

    override fun equals(o: Any?): Boolean {
        return equals(o, false)
    }

    fun equals(o: Any?, ignoreProtocol: Boolean): Boolean {
        if (this === o) return true
        if (o !is GitCodeServerPath) return false
        return (ignoreProtocol || myUseHttp == o.myUseHttp) &&
                host == o.host &&
                port == o.port &&
                suffix == o.suffix
    }

    override fun hashCode(): Int {
        return Objects.hash(host, port, suffix)
    }

    companion object {
        const val DEFAULT_HOST: String = "gitcode.com"
        val DEFAULT_SERVER: GitCodeServerPath = GitCodeServerPath(DEFAULT_HOST)
        private const val API_PREFIX = "api."
        private const val API_SUFFIX = "/api/v5"
        private const val ENTERPRISE_API_SUFFIX = "/api/v5"


        // 1 - schema, 2 - host, 4 - port, 5 - path
        private val URL_REGEX: Pattern = Pattern.compile(
            "^(https?://)?([^/?:]+)(:(\\d+))?((/[^/?#]+)*)?/?",
            Pattern.CASE_INSENSITIVE
        )

        @JvmStatic
        @Throws(GitCodeParseException::class)
        fun from(uri: String): GitCodeServerPath {
            val matcher = URL_REGEX.matcher(uri)

            if (!matcher.matches()) throw GitCodeParseException("Not a valid URL")
            val schema = matcher.group(1)
            val httpSchema =
                if (schema == null || schema.isEmpty()) null else schema.equals("http://", ignoreCase = true)
            val host = matcher.group(2) ?: throw GitCodeParseException("Empty host")

            val port: Int?
            val portGroup = matcher.group(4)
            port = if (portGroup == null) {
                null
            } else {
                try {
                    portGroup.toInt()
                } catch (e: NumberFormatException) {
                    throw GitCodeParseException("Invalid port format")
                }
            }

            val path = StringUtil.nullize(matcher.group(5))

            return GitCodeServerPath(httpSchema, host, port, path)
        }
    }
}
